#include <vector>
#include <string>
#include <boost/unordered/unordered_map.hpp> 

#include "GeneralVisitor.h"
#include "ParseHelper.h"
#include "../common/Instruction.h"

using namespace boost;

class CompilerVisitor: public GeneralVisitor
{
public:
    CompilerVisitor(const ParseHelper& helper);
    virtual ~CompilerVisitor();

    virtual void Visit(ArrayAccess* iNode);
    virtual void Visit(BinaryOperation* iNode);
    virtual void Visit(ConstantValue* iNode);
    virtual void Visit(FieldAccess* iNode);
    virtual void Visit(UnaryOperation* iNode);
    virtual void Visit(Variable* iNode);
    virtual void Visit(Assign* iNode);
    virtual void Visit(EmptyStatement* iNode);
    virtual void Visit(ForLoop* iNode);
    virtual void Visit(IfStatement* iNode);
    virtual void Visit(NewExpression* iNode);
    virtual void Visit(ReturnStatement* iNode);
    virtual void Visit(StatementList* iNode);
    virtual void Visit(VarDeclaration* iNode);
    virtual void Visit(WhileLoop* iNode);
    virtual void Visit(FunctionCall* iNode);
    virtual void Visit(MethodCall* iNode);
    virtual void Visit(ClassDefinition* iNode);
    virtual void Visit(FunctionDefinition* iNode);
    virtual void Visit(MethodDefinition* iNode);

	void Serialize(ostream& out);


private:
	typedef vector<string> StringPool;

    void FillStringPool();
    void OutputStringPool();
    void OutputFunctions();
    void OutputClasses();

    void BuildClassDefinition(ClassDefinition* iClass, vector<char>& oTarget);
    void BuildFieldDefinitions(ClassDefinition* iClass, vector<char>& oTarget);
    void BuildMethodDefinitions(ClassDefinition* iClass, vector<char>& oTarget);

    void AppendUnsignedAsLittleEndian(unsigned int iValue, vector<char>& oTarget) const;
    void AppendSignedAsLittleEndian(int iValue, vector<char>& oTarget) const;
    void ReplaceInt(vector<char>& oWhere, unsigned int iStart, int iReplacement);
	unsigned int GetNameIndex(const string& name) const;
	void Generate(Instruction i, int arg1 = 0, int arg2 = 0);

    void ProcessBackPatches();

    StringPool pool;
	set<ClassDefinition*> classes;
	set<FunctionDefinition*> functions;
    vector<string*> fStringConstants;
    vector<char> fWholeCode;
	vector<char> bytecode;
    unordered_map<string, signed char> fLocalVars;
    vector<pair<unsigned int, FunctionDefinition*> > fFuncAddrBackPatches;
    FunctionDefinition* fEntryPoint;
    vector<string> fCalledFunctionNames;
    bool fIsInConstructorContext;
};
